<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-us" lang="en-us">
<head>
  <link href="//gmpg.org/xfn/11" rel="profile">
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <meta name="generator" content="Hugo 0.68.3" />

  
  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>python中的锁 &middot; 我的博客</title>

  
  <link type="text/css" rel="stylesheet" href="/my_technology_blog/css/print.css" media="print">
  <link type="text/css" rel="stylesheet" href="/my_technology_blog/css/poole.css">
  <link type="text/css" rel="stylesheet" href="/my_technology_blog/css/syntax.css">
  <link type="text/css" rel="stylesheet" href="/my_technology_blog/css/hyde.css">
    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Abril+Fatface|PT+Sans:400,400i,700">


  
  <link rel="apple-touch-icon-precomposed" sizes="144x144" href="/apple-touch-icon-144-precomposed.png">
  <link rel="shortcut icon" href="/favicon.png">

  
  
</head>

  <body class="theme-base-0b ">
  <aside class="sidebar">
  <div class="container sidebar-sticky">
    <div class="sidebar-about">
      <a href="/my_technology_blog/"><h1>我的博客</h1></a>
      <p class="lead">
       杨博的博客 
      </p>
    </div>

    <nav>
      <ul class="sidebar-nav">
        <li><a href="/my_technology_blog/">Home</a> </li>
        
      </ul>
    </nav>

    <p>&copy; 2021. All rights reserved. </p>
  </div>
</aside>

    <main class="content container">
    <div class="post">
  <h1>python中的锁</h1>
  <time datetime=2020-06-29T10:48:53&#43;0800 class="post-date">Mon, Jun 29, 2020</time>
  <p><strong>一、全局解释器锁（GIL）</strong>，<strong>二、同步锁</strong>，  <strong>三、递归锁和死锁</strong>，  <strong>四、信号量（semaphore）</strong></p>
<h2 id="一全局解释器锁gil">一、全局解释器锁（GIL）</h2>
<p><!-- raw HTML omitted -->全局解释器锁能够保证同一时刻只有一个线程在运行，避免了线程之间相互争夺资源<!-- raw HTML omitted --></p>
<p>Python 虚拟机默认使用的是 CPython 解释器(C 语言实现)，CPython 使用了 GIL (Golbal Iterpreter Lock - 全局解释器锁)，来确保同一时间只有一个线程运行，所以即使再多的线程也只能有效的使用一个 CPU。</p>
<p><strong><!-- raw HTML omitted -->为什么不删除 GIL:<!-- raw HTML omitted --></strong></p>
<p>实验证明，如果放弃 GIL，使用大量细粒度的锁代替，导致单线程性能下降至少 30%。所以说 GIL 在支持多线程的同时能把单线程的优势最大地发挥出来。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python"><span style="color:#f92672">import</span> time
<span style="color:#f92672">import</span> threading


<span style="color:#66d9ef">def</span> <span style="color:#a6e22e">sub</span>():
    <span style="color:#66d9ef">global</span> num
    num <span style="color:#f92672">-=</span> <span style="color:#ae81ff">1</span>
    time<span style="color:#f92672">.</span>sleep(<span style="color:#ae81ff">1</span>)
num <span style="color:#f92672">=</span> <span style="color:#ae81ff">100</span>  <span style="color:#75715e"># 定义一个全局变量</span>
l <span style="color:#f92672">=</span> []  <span style="color:#75715e"># 定义一个空列表，用来存放所有的列表</span>
<span style="color:#66d9ef">for</span> i <span style="color:#f92672">in</span> range(<span style="color:#ae81ff">100</span>):  <span style="color:#75715e"># for循环100次</span>
    t <span style="color:#f92672">=</span> threading<span style="color:#f92672">.</span>Thread(target<span style="color:#f92672">=</span>sub)  <span style="color:#75715e">#每次循环开启一个线程</span>
    t<span style="color:#f92672">.</span>start()  <span style="color:#75715e"># 开启线程</span>
    l<span style="color:#f92672">.</span>append(t)  <span style="color:#75715e"># 将线程加入列表l</span>
<span style="color:#66d9ef">for</span> i <span style="color:#f92672">in</span> l:
    i<span style="color:#f92672">.</span>join()  <span style="color:#75715e"># 这里加上join保证所有的线程结束后才运行下面的代码</span>
<span style="color:#66d9ef">print</span>(num)
<span style="color:#75715e"># 输出结果为0</span>
</code></pre></div><h2 id="二线程锁">二、线程锁</h2>
<p>　	当一个线程对某个资源进行CPU计算的操作时加一个线程锁，只有当前线程计算完成主动释放锁，其他线程才能对其操作</p>
<p>​		这样就可以防止还未计算完成，释放GIL锁后其他线程对这个资源操作导致混乱问题</p>
<p>实例：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python"><span style="color:#f92672">import</span> time
<span style="color:#f92672">import</span> threading
lock <span style="color:#f92672">=</span> threading<span style="color:#f92672">.</span>Lock()          <span style="color:#75715e">#1 生成全局锁</span>
<span style="color:#66d9ef">def</span> <span style="color:#a6e22e">addNum</span>():
    <span style="color:#66d9ef">global</span> num                  <span style="color:#75715e">#2 在每个线程中都获取这个全局变量</span>
    <span style="color:#66d9ef">print</span>(<span style="color:#e6db74">&#39;--get num:&#39;</span>,num )
    time<span style="color:#f92672">.</span>sleep(<span style="color:#ae81ff">1</span>)
    lock<span style="color:#f92672">.</span>acquire()              <span style="color:#75715e">#3 修改数据前加锁</span>
    num  <span style="color:#f92672">-=</span> <span style="color:#ae81ff">1</span>                   <span style="color:#75715e">#4 对此公共变量进行-1操作</span>
    lock<span style="color:#f92672">.</span>release()              <span style="color:#75715e">#5 修改后释放</span>

</code></pre></div><p><strong>5、扩展知识</strong></p>
<p>　　　　1、GIL的作用：多线程情况下必须存在资源的竞争，GIL是为了保证在解释器级别的线程唯一使用共享资源（cpu）。</p>
<p>　　　　2、同步锁的作用：为了保证解释器级别下的自己编写的程序唯一使用共享资源产生了同步锁。</p>
<h2 id="三递归锁和死锁">三、递归锁和死锁</h2>
<p>　　<strong>1、什么是死锁？</strong></p>
<p>　　　　是指两个或两个以上的进程或线程在执行过程中，因争夺资源而造成的一种互相等待的现象，若无外力作用，它们都将无法推进下去。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python"><span style="color:#f92672">import</span> time
<span style="color:#f92672">import</span> threading

A <span style="color:#f92672">=</span> threading<span style="color:#f92672">.</span>Lock()
B <span style="color:#f92672">=</span> threading<span style="color:#f92672">.</span>Lock()
<span style="color:#f92672">import</span> threading


<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">obj</span>(threading<span style="color:#f92672">.</span>Thread):
    <span style="color:#66d9ef">def</span> __init__(self):
        super()<span style="color:#f92672">.</span>__init__()

    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">run</span>(self):
        self<span style="color:#f92672">.</span>a()   <span style="color:#75715e"># 如果两个锁同时被多个线程运行，就会出现死锁现象</span>
        self<span style="color:#f92672">.</span>b()
    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">a</span>(self):
        A<span style="color:#f92672">.</span>acquire()
        <span style="color:#66d9ef">print</span>(<span style="color:#e6db74">&#39;123&#39;</span>)
        B<span style="color:#f92672">.</span>acquire()
        <span style="color:#66d9ef">print</span>(<span style="color:#ae81ff">456</span>)
        time<span style="color:#f92672">.</span>sleep(<span style="color:#ae81ff">1</span>)
        B<span style="color:#f92672">.</span>release()
        <span style="color:#66d9ef">print</span>(<span style="color:#e6db74">&#39;qweqwe&#39;</span>)
        A<span style="color:#f92672">.</span>release()
    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">b</span>(self):
        B<span style="color:#f92672">.</span>acquire()
        <span style="color:#66d9ef">print</span>(<span style="color:#e6db74">&#39;asdfaaa&#39;</span>)
        A<span style="color:#f92672">.</span>acquire()
        <span style="color:#66d9ef">print</span>(<span style="color:#e6db74">&#39;(⊙o⊙)哦(⊙v⊙)嗯&#39;</span>)
        A<span style="color:#f92672">.</span>release()
        B<span style="color:#f92672">.</span>release()
<span style="color:#66d9ef">for</span> i <span style="color:#f92672">in</span> range(<span style="color:#ae81ff">2</span>):  <span style="color:#75715e"># 循环两次，运行四个线程，第一个线程成功处理完数据，第二个和第三个就会出现死锁</span>
    t <span style="color:#f92672">=</span> obj()
    t<span style="color:#f92672">.</span>start()
</code></pre></div><p>程序会出现阻塞现象</p>
<p>　<strong>2、什么是递归锁？</strong></p>
<p>　　　　在Python中为了支持同一个线程中多次请求同一资源，Python提供了可重入锁。这个锁内部维护着一个Lock和一个counter变量，counter记录了请求到的次数，从而使得资源可以被多次请求。直到锁都被释放，其他的线程才能获得资源。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python"><span style="color:#f92672">import</span> time
<span style="color:#f92672">import</span> threading

A <span style="color:#f92672">=</span> threading<span style="color:#f92672">.</span>RLock()  <span style="color:#75715e"># 这里设置锁为递归锁</span>
<span style="color:#f92672">import</span> threading


<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">obj</span>(threading<span style="color:#f92672">.</span>Thread):
    <span style="color:#66d9ef">def</span> __init__(self):
        super()<span style="color:#f92672">.</span>__init__()

    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">run</span>(self):
        self<span style="color:#f92672">.</span>a()
        self<span style="color:#f92672">.</span>b()
  
    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">a</span>(self): <span style="color:#75715e"># 递归锁，就是将多个锁的钥匙放到一起，要拿就全拿，要么一个都拿不到</span>
                <span style="color:#75715e"># 以实现锁</span>
        A<span style="color:#f92672">.</span>acquire()
        <span style="color:#66d9ef">print</span>(<span style="color:#e6db74">&#39;123&#39;</span>)
        <span style="color:#66d9ef">print</span>(<span style="color:#ae81ff">456</span>)
        time<span style="color:#f92672">.</span>sleep(<span style="color:#ae81ff">1</span>)
        <span style="color:#66d9ef">print</span>(<span style="color:#e6db74">&#39;qweqwe&#39;</span>)
        A<span style="color:#f92672">.</span>release()
    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">b</span>(self):
        A<span style="color:#f92672">.</span>acquire()
        <span style="color:#66d9ef">print</span>(<span style="color:#e6db74">&#39;asdfaaa&#39;</span>)
        <span style="color:#66d9ef">print</span>(<span style="color:#e6db74">&#39;(⊙o⊙)哦(⊙v⊙)嗯&#39;</span>)
        A<span style="color:#f92672">.</span>release()
<span style="color:#66d9ef">for</span> i <span style="color:#f92672">in</span> range(<span style="color:#ae81ff">2</span>):
    t <span style="color:#f92672">=</span> obj()
    t<span style="color:#f92672">.</span>start()
</code></pre></div><h2 id="四信号量semaphore">四、信号量（semaphore）</h2>
<p>　　<strong>1、什么是信号量？</strong></p>
<p>　　　　同进程的一样，semaphore管理一个内置的计数器，每当调用acquire()时内置函数-1，每当调用release()时内置函数+1。</p>
<p>　　　计数器不能为0，当计数器为0时acquire（）将阻塞线程，直到其他线程调用release（）。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python"><span style="color:#f92672">import</span> threading
<span style="color:#f92672">import</span> time

mysf <span style="color:#f92672">=</span> threading<span style="color:#f92672">.</span>Semaphore(<span style="color:#ae81ff">5</span>)  <span style="color:#75715e"># 创建信号量对象,(5表示这个锁同时支持的个数)</span>


<span style="color:#66d9ef">def</span> <span style="color:#a6e22e">func</span>():
    <span style="color:#66d9ef">if</span> mysf<span style="color:#f92672">.</span>acquire():  <span style="color:#75715e"># 因为使用了信号量，下面的输出就会5个5个的同时输出</span>
        <span style="color:#66d9ef">print</span>(threading<span style="color:#f92672">.</span>currentThread()<span style="color:#f92672">.</span>getName() <span style="color:#f92672">+</span> <span style="color:#e6db74">&#39;get semaphore&#39;</span>)  
        time<span style="color:#f92672">.</span>sleep(<span style="color:#ae81ff">1</span>)
        mysf<span style="color:#f92672">.</span>release()
<span style="color:#66d9ef">for</span> i <span style="color:#f92672">in</span> range(<span style="color:#ae81ff">20</span>):
    t <span style="color:#f92672">=</span> threading<span style="color:#f92672">.</span>Thread(target<span style="color:#f92672">=</span>func)
    t<span style="color:#f92672">.</span>start()
</code></pre></div>
</div>


    </main>

    
  </body>
</html>
